using UnityEngine;
using System.Collections;
using System.Collections.Generic;

[ExecuteInEditMode]
public class BezierChainNavigator : MonoBehaviour
{
	#region Variables
	
	// Steps
	public		BezierStepNavigator 	m_bsnFirstStep;
	protected	BezierStepNavigator 	m_bsnCurStep;
	
	// Start
	public	bool						m_bAutoStart					= false;
	public	bool						m_bInitPath						= true;
	
	// Traveller
	public	Transform					m_tObjectToMove;
	
	// Traveling
	[HideInInspector]
	public	float						m_fPercentageCur;
	private float						m_fSpeedReal;
	public 	bool						m_bIsLoop;
	public 	bool						m_bSpeedMultCurveUse;
	private AnimationCurve				m_acSpeedMult;
	
	// Orienting
	private Vector3						m_v3Direction;
	
	private bool						m_bOrientForward;
	private	Vector3						m_v3ForwardOrigin;
	private	Vector3						m_v3ForwardCurrent;
	private bool						m_bOrientingForwardSpecific;
	private	Vector3						m_v3ForwardFuture;
	private bool						m_bBlendingForward;
	private	float						m_fBlendingForwardPercentToComplete;
	private	AnimationCurve				m_acBlendForwardCurve;
	
	private bool						m_bOrientUp;
	private	Vector3						m_v3UpOrigin;
	private	Vector3						m_v3UpEnd;
	private	Vector3						m_v3UpCurrent;
	private	AnimationCurve				m_acBlendUpCurve;
	
	// debug
	public	bool						m_bDebugDrawCurve				=	true;
	public	bool						m_bDebugUpdateCurvePerFtrame	=	false;
	
	#endregion
	
	
	//---------------------------------------------------------------------------------------------------------
	//---------------------------------------------------------------------------------------------------------
	void Activate(bool _bOn)
	{
		this.enabled = _bOn;
	}
	
	//---------------------------------------------------------------------------------------------------------
	//---------------------------------------------------------------------------------------------------------
	public bool IsActive()
	{
		return this.enabled;
	}
	
	//---------------------------------------------------------------------------------------------------------
	//---------------------------------------------------------------------------------------------------------
	virtual protected void Awake ()
	{
		// Init basic data
    	//---------------------------------------------------------------------------------------------------------
		Start();
		
		
		// Init the first bezier step and allow it to chain all the curve
    	//---------------------------------------------------------------------------------------------------------
		if(m_bInitPath)
		{
			m_bsnFirstStep.Init();
		}
		
		
		// Init orientation data
    	//---------------------------------------------------------------------------------------------------------
		GetOrientationData();
		
		
		// Get the default object to move if there is none
    	//---------------------------------------------------------------------------------------------------------
		if(!m_tObjectToMove)
		{
			m_tObjectToMove = transform;
		}
	}
	
	
	//---------------------------------------------------------------------------------------------------------
	//---------------------------------------------------------------------------------------------------------
	void Start()
	{
		m_fPercentageCur 	=	0.0f;
		m_fSpeedReal		=	0.0f;
		m_bsnCurStep		=	m_bsnFirstStep;
		
		// Activate
    	//---------------------------------------------------------------------------------------------------------
		if(!m_bAutoStart)
		{
			Activate(true);
		}
	}	
	
	
	//---------------------------------------------------------------------------------------------------------
	//---------------------------------------------------------------------------------------------------------
	protected virtual void Update ()
	{
		// Update the speed
    	//---------------------------------------------------------------------------------------------------------
		GetSpeedBasedOnPercentage(m_fPercentageCur);
		
		
		// increase the percentage through speed
    	//---------------------------------------------------------------------------------------------------------
		float time	=	Time.deltaTime;
		m_fPercentageCur 		+=	m_fSpeedReal * Time.deltaTime;
		
		
		// Check change of step
    	//---------------------------------------------------------------------------------------------------------
		while(m_fPercentageCur	>	1.0f)
		{
			// Reduce the percentage to zero
			m_fPercentageCur 	= 	0.0f;
			
			// Get the time that remained after the 1.0 perc was reached
			float fTimeLeft		=	m_fPercentageCur / m_fSpeedReal;
			
			// Change and update all the variables
			if(ReachSegmentEnd())
			{
				// if the end has been reached make sure that the last position is achieved before stopping it
				m_fPercentageCur	=	1.0f;
			}
			
			// Get the new speed
			GetSpeedBasedOnPercentage(m_fPercentageCur);
			
			// Add the remaining percentage with the remaining time and new speed
			m_fPercentageCur	+=	m_fSpeedReal * fTimeLeft;
		}
		
		
		// Set the object on place
    	//---------------------------------------------------------------------------------------------------------
		// use it to orient the ship later
		m_v3Direction				=	m_bsnCurStep.m_bCurve.GetPointAtTime(m_fPercentageCur) - m_tObjectToMove.position; 
		m_tObjectToMove.position	+=	m_v3Direction;
		
		
		// Orient if needed
    	//---------------------------------------------------------------------------------------------------------
		if(m_bOrientForward)
		{
			if(m_bOrientingForwardSpecific)
			{
				m_v3ForwardCurrent	= Vector3.Lerp(m_v3ForwardOrigin , m_v3ForwardFuture
		                                          	, m_acBlendForwardCurve.Evaluate(m_fPercentageCur));
			}
			else if(m_bBlendingForward)
			{
				if(m_fPercentageCur >= m_fBlendingForwardPercentToComplete)
				{
					m_bBlendingForward	= false;
					m_v3ForwardCurrent	= m_v3Direction;
				}
				else
				{
					float	fPercentage	= Maths.FloatMap(0.0f,  m_fBlendingForwardPercentToComplete, 0.0f, 1.0f, m_fPercentageCur);
					m_v3ForwardCurrent	= Vector3.Lerp(m_v3ForwardOrigin , m_v3Direction, m_acBlendForwardCurve.Evaluate(fPercentage));
				}
			}
			else
			{
				m_v3ForwardCurrent	= m_v3Direction;
			}
		}
		if(m_bOrientUp)
		{ 
			m_v3UpCurrent		= Vector3.Lerp(m_v3UpOrigin, m_v3UpEnd
			                                            	, m_acBlendUpCurve.Evaluate(m_fPercentageCur));
		}
		if(m_bOrientForward || m_bOrientUp)
		{
			m_tObjectToMove.rotation = Quaternion.LookRotation(m_v3ForwardCurrent, m_v3UpCurrent);
		}
		
		// Debug stuff
    	//---------------------------------------------------------------------------------------------------------
		if(m_bDebugUpdateCurvePerFtrame)
		{
			m_bsnFirstStep.Init();
		}
		if(m_bDebugDrawCurve) 
		{
			m_bsnFirstStep.DrawAllCurve();
		}
	}
	
	//---------------------------------------------------------------------------------------------------------
	//---------------------------------------------------------------------------------------------------------
	private bool ReachSegmentEnd()
	{
		if(!m_bsnCurStep.m_bIsLast || m_bIsLoop)
		{
			// Get the next segment (step)
			m_bsnCurStep		=	m_bsnCurStep.m_bsnDestination;
			
			// Get orientation Data
			GetOrientationData();
			return false;
		}
		else
		{
			Activate(false);
			return true;
		}
	}
	
	
	//---------------------------------------------------------------------------------------------------------
	//---------------------------------------------------------------------------------------------------------
	private void GetSpeedBasedOnPercentage(float _fPercentage)
	{
		// Get the blended speed
    	//---------------------------------------------------------------------------------------------------------
		m_fSpeedReal				= Maths.FloatBlend	( m_bsnCurStep.m_fSpeedInit
			                                  			, m_bsnCurStep.m_fSpeedEnd
			                                   			, m_fPercentageCur);
		
		// And apply the speed curve multiplier
    	//---------------------------------------------------------------------------------------------------------
		if(m_bSpeedMultCurveUse)
		{
			m_fSpeedReal			*=	m_acSpeedMult.Evaluate(m_fPercentageCur);
		}
		
		// Apply the speed shape curve multiplier
    	//---------------------------------------------------------------------------------------------------------
		if(m_bsnCurStep.m_bUseSpeedShapeCurve)
		{
			m_fSpeedReal			*=	m_bsnCurStep.m_acSpeedShapeCurve.Evaluate(m_fPercentageCur);
			// TODO
			// if the speed real is long enough to make the percentage current cross the keyframe of 
			// the next speed in the speed shape curve, change the speed to take it into account
		}
	}
	
	
	//---------------------------------------------------------------------------------------------------------
	//---------------------------------------------------------------------------------------------------------
	protected virtual void GetOrientationData()
	{
		// Get the speed multiplyer curve
		m_bSpeedMultCurveUse		=	m_bsnCurStep.m_bUseSpeedBlendCrve;
		m_acSpeedMult				=	m_bsnCurStep.m_acSpeedMultCurve;
		
		
		// Check what needs to be blended
		m_bBlendingForward			=	(m_bOrientForward && !m_bsnCurStep.m_bOrientForward) || m_bOrientingForwardSpecific;
		m_bOrientForward			=	m_bsnCurStep.m_bOrientForward;
		m_v3ForwardOrigin			=	m_tObjectToMove.forward;
		
		
		if(m_bOrientForward)
		{
			// Check if destination requires a specific forward orientation and store the one required
			if(m_bsnCurStep.m_bsnDestination)
			{
				m_bOrientingForwardSpecific	= m_bsnCurStep.m_bsnDestination.m_bForwardOrientSpecific;
			}
			else
			{
				m_bOrientingForwardSpecific	= false;
			}
			
			if(m_bOrientingForwardSpecific)
			{
				m_v3ForwardFuture	=	m_bsnCurStep.m_bsnDestination.m_tForwardSpecific.forward;
			}
			else
			{
				m_fBlendingForwardPercentToComplete = m_bsnCurStep.m_fBlendingForwardPercentToComplete;
			}
			
			m_acBlendForwardCurve	=	m_bsnCurStep.m_acBlendForwardCurve;
		}
		
		// Store the up orientation
		m_bOrientUp					=	m_bsnCurStep.m_bOrientUp;
		if(m_bOrientUp)
		{
			m_v3UpOrigin			=	m_tObjectToMove.up;
			m_v3UpEnd				=	m_bsnCurStep.m_bsnDestination.transform.up;
			//m_fBlendUpTotalAngle	=	Vector3.Angle(m_v3UpOrigin, m_v3UpEnd) * Mathf.Deg2Rad;
			m_acBlendUpCurve		=	m_bsnCurStep.m_acBlendUpCurve;
		}
	}
}
